package com.google.code.retrobox.editor.sprites;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class SpriteIO {

	private static final String SPRITE = "sprite";
	private static final String SPRITE_SHEET = "spriteSheet";

	public void save(File file, SpriteEditor sp) throws Exception {

		String path = file.getAbsolutePath();
		if (!path.endsWith(".xml")) {
			path = path + ".xml";
			file = new File(path);
		}

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.newDocument();

		Element root = doc.createElement(SPRITE_SHEET);
		root.setAttribute("version", "1.0");
		doc.appendChild(root);

		char[] data = sp.getRawData();

		// each sprite is 16 x 16 x 4 -> 2 bytes x 16 rows x 4 color bits
		for (int t = 0; t < data.length; ) {
			//System.out.println("data:" + data.length + ", t:" + t);
			Element sprite = doc.createElement(SPRITE);
			String spriteData = "";
			for (int d = 0; d < 128; d++) {
				if (d > 0) {
					spriteData += ", ";
				}
				spriteData += String.format("0x%X", (int) data[t + d]);
			}
			sprite.appendChild(doc.createTextNode(spriteData));
			root.appendChild(sprite);
			t += 128;
		}

		// write the content into xml file
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(file);
		transformer.transform(source, result);

	}

	public void load(File file, SpriteEditor sp) throws Exception {

		List<Character> data = new ArrayList<Character>();

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(file);
		doc.getDocumentElement().normalize();
		System.out.println("Root element "
				+ doc.getDocumentElement().getNodeName());
		if (!doc.getDocumentElement().getNodeName().equals(SPRITE_SHEET)) {
			throw new Exception("Unknown file format, illegal tag: "
					+ doc.getDocumentElement().getNodeName());
		}
		NodeList spriteList = doc.getElementsByTagName(SPRITE);
		// handle sprite elements

		if (spriteList == null || spriteList.getLength() == 0) {
			throw new Exception("Illegal file format, no sprites found.");
		}
		for (int s = 0; s < spriteList.getLength(); s++) {

			Node spriteNode = spriteList.item(s);
			String spriteData = spriteNode.getTextContent();
			Scanner sc = new Scanner(spriteData);

			while (sc.hasNext()) {
				String next;

				if ((next = sc.next()).toLowerCase().startsWith("0x")) {
					System.out.println("data:" + next);
					int lastIndex = next.length();
					int tmp = next.lastIndexOf(",");
					if (tmp > -1) {
						lastIndex = tmp;
					}
					try {
						data.add((char) Integer.parseInt(next.substring(2,
								lastIndex), 16));
					} catch (Exception e) {
						throw new Exception(
								"Error while parsing sprite data from file.");
					}
				}
			}
		}

		char[] dataArray = new char[data.size()];

		for (int i = 0; i < data.size(); i++) {
			dataArray[i] = data.get(i);
		}
		sp.setSprite(dataArray);
	}

	public void importFromHeader(File file, SpriteEditor se) throws Exception {

		List<Character> data = new ArrayList<Character>();

		try {
			FileInputStream fstream = new FileInputStream(file);
			DataInputStream in = new DataInputStream(fstream);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));

			String line;
			while ((line = br.readLine()) != null) {
				Scanner sc = new Scanner(line);

				if (true) {
					while ((line = br.readLine()) != null) {
						if (line.indexOf("#define") >= 0) {
							while ((line = br.readLine()) != null) {
								if (line.indexOf("/*") >= 0) {
									continue;
								}
								sc = new Scanner(line);

								while (sc.hasNext()) {
									String next;
									if ((next = sc.next()).toLowerCase()
											.startsWith("0x")) {
										int lastIndex = next.length();
										int tmp = next.lastIndexOf(",");
										if (tmp > -1) {
											lastIndex = tmp;
										}
										try {
											data.add((char) Integer.parseInt(
													next
															.substring(2,
																	lastIndex),
													16));
										} catch (Exception e) {
											if (in != null) {
												in.close();
											}
											throw new Exception(
													"Error parsing hex from text file");
										}
									}
								}
							}

							break;
						}
					}
					break;
				}
			}
			in.close();

		} catch (Exception e) {// Catch exception if any
			throw new Exception("Error importing file.", e);
		}

		char[] dataArray = new char[data.size()];

		for (int i = 0; i < data.size(); i++) {
			dataArray[i] = data.get(i);
		}

		se.setSprite(dataArray);

	}

	public void exportToHeader(File file, SpriteEditor se) throws Exception {
		try {
			String path = file.getAbsolutePath();
			if (!path.endsWith(".h")) {
				path = path + ".h";
			}

			BufferedWriter out = new BufferedWriter(new FileWriter(path));
			out.write("// Auto generated map header file. RetroBox IDE. \n\n" +

			"// Sprite properties: \n" +

			"// Width: " + SpriteRenderer.SPRITES_X + "\n" + "// Height: "
					+ SpriteRenderer.SPRITES_Y + "\n\n" +

					"#define SPRITE16 { \n");

			int i = 1;
			for (char element : se.getRawData()) {
				out.write(String.format("0x%X, ", (int) element));
				if (i == 8) {
					out.write("\n");
					i = 0;
				}
				i++;
			}

			out.write("}");

			out.close();

		} catch (IOException e) {
			throw new Exception("Error while exporting file.", e);
		}
	}
	
	public void exportToGameduinoHeader(File file, SpriteEditor se) throws Exception {
		try {
			String path = file.getAbsolutePath();
			if (!path.endsWith(".h")) {
				path = path + ".h";
			}

			BufferedWriter out = new BufferedWriter(new FileWriter(path));
			out.write("// Auto generated map header file. RetroBox IDE. \n\n" +

			"// Sprite properties: \n" +

			"// Width: " + SpriteRenderer.SPRITES_X + "\n" + "// Height: "
					+ SpriteRenderer.SPRITES_Y + "\n\n" +

					"static PROGMEM prog_uchar spritebitboy_sprimg[] = { \n");

			int i = 1;
			for (char element : se.getRawDataAsByteArray()) {
				out.write(String.format("0x%X, ", (int) element));
				if (i == 16) {
					out.write("\n");
					i = 0;
				}
				i++;
			}

			out.write("}");

			out.close();

		} catch (IOException e) {
			throw new Exception("Error while exporting file.", e);
		}
	}
}
